import array
import struct
import threading
import time
import tkinter as tk
from tkinter import ttk

import cv2
from ultralytics import YOLO
from app.DataParser import DataParser
from app.MspHelper import MspHelper
from app.RobustOpticalFlow import RobustOpticalFlow
from myserial import CommService
from mychart.DynamicChart import DynamicChart
import myserial
import socket

class WinApp:
    def __init__(self):
        self.mspHelper = MspHelper()
        self.btn_compass_calibration = None
        self.btn_acc_calibration = None
        self.dcAcc = None
        self.lbl_batteryVoltage = None
        self.lbl_altSonar = None
        self.dataParser = DataParser(self.oneFrameEvent)
        # 创建socket对象
        self.btn_tcp_open = None
        self.socket_client = None
        self.tcpRunning = False
        self.dcAltitude = None
        self.btn_open = None
        self.comm_service = None
        self.cbox = None
        self.lbl_altitude_target = None
        self.lbl_altitude = None
        self.lbl_throttle = None
        self.lbl_yaw = None
        self.lbl_roll = None
        self.lbl_pitch = None
        self.lbl_gpsNumSat = None
        self.win = tk.Tk()
        self.win.title('调试地面站')
        self.win.geometry('900x600')

        self.panedWindow = tk.PanedWindow(self.win)
        self.panedWindow.pack(fill=tk.BOTH, expand=True)

        self.leftFrame = tk.Frame(self.panedWindow, width=100, height=100, bg='red', pady=5, padx=5)
        self.rightFrame = tk.Frame(self.panedWindow, width=100, height=100, bg='blue', pady=10,padx=5)
        self.panedWindow.add(self.leftFrame)
        self.panedWindow.add(self.rightFrame)

        self.btn_test = None



        self.initUI()

    def initUI(self):
        # --------------------姿态信息布局控件--------------------------
        lblFlyStateFrame = tk.LabelFrame(self.rightFrame,text='姿态信息')
        lblFlyStateFrame.pack(fill=tk.X)

        tk.Label(lblFlyStateFrame, text="俯仰", width=10).grid(row=1, column=1)
        self.lbl_pitch = tk.Label(lblFlyStateFrame, text="-", width=10,font=("Helvetica", 16))
        self.lbl_pitch.grid(row=2, column=1)

        tk.Label(lblFlyStateFrame, text="横滚", width=10).grid(row=1, column=2)
        self.lbl_roll = tk.Label(lblFlyStateFrame, text="-", width=10,font=("Helvetica", 16))
        self.lbl_roll.grid(row=2, column=2)

        tk.Label(lblFlyStateFrame, text="偏航", width=10).grid(row=1, column=3)
        self.lbl_yaw = tk.Label(lblFlyStateFrame, text="-", width=10,font=("Helvetica", 16))
        self.lbl_yaw.grid(row=2, column=3)

        tk.Label(lblFlyStateFrame, text="油门", width=10).grid(row=1, column=4)
        self.lbl_throttle = tk.Label(lblFlyStateFrame, text="-", width=10,font=("Helvetica", 16))
        self.lbl_throttle.grid(row=2, column=4)

        tk.Label(lblFlyStateFrame, text="高度", width=10).grid(row=1, column=5)
        self.lbl_altitude = tk.Label(lblFlyStateFrame, text="-", width=10,font=("Helvetica", 16))
        self.lbl_altitude.grid(row=2, column=5)

        tk.Label(lblFlyStateFrame, text="目标高度", width=10).grid(row=1, column=6)
        self.lbl_altitude_target = tk.Label(lblFlyStateFrame, text="-", width=10,font=("Helvetica", 16))
        self.lbl_altitude_target.grid(row=2, column=6)

        tk.Label(lblFlyStateFrame, text="卫星数量", width=10).grid(row=1, column=7)
        self.lbl_gpsNumSat = tk.Label(lblFlyStateFrame, text="-", width=10,font=("Helvetica", 16))
        self.lbl_gpsNumSat.grid(row=2, column=7)

        tk.Label(lblFlyStateFrame, text="超声波", width=10).grid(row=1, column=8)
        self.lbl_altSonar = tk.Label(lblFlyStateFrame, text="-", width=10, font=("Helvetica", 16))
        self.lbl_altSonar.grid(row=2, column=8)

        tk.Label(lblFlyStateFrame, text="电压", width=10).grid(row=1, column=9)
        self.lbl_batteryVoltage = tk.Label(lblFlyStateFrame, text="-", width=10, font=("Helvetica", 16))
        self.lbl_batteryVoltage.grid(row=2, column=9)

        # --------------------姿态信息布局控件--------------------------

        # --------------------串口初始化-------------------------------
        self.cbox = ttk.Combobox(self.leftFrame)
        self.cbox.pack(fill=tk.X, expand=False)
        self.cbox.bind("<<ComboboxSelected>>", self.ev_com_sel)

        # --------------------串口按钮---------------------------------
        btn_refresh = ttk.Button(self.leftFrame, text='刷新串口', command=self.ev_btn_com_refresh)
        btn_refresh.pack(fill=tk.X)

        self.comm_service = CommService(self.cbox.get())

        # 刷新串口列表按
        self.btn_open = ttk.Button(self.leftFrame, text='打开串口', command=self.ev_btn_com_open)
        self.btn_open.pack(fill=tk.X)

        # 动态图表
        lblAltFrame = tk.LabelFrame(self.rightFrame,text='气压计高度')
        lblAltFrame.pack(fill=tk.X,pady=5)
        self.dcAltitude = DynamicChart(lblAltFrame,100,45,
                                       3,1,7,2,['Alt','Exp Alt'])

        lblAccFrame = tk.LabelFrame(self.rightFrame,text='加速度计')
        lblAccFrame.pack(fill=tk.X,pady=5)
        self.dcAcc = DynamicChart(lblAccFrame,100,45,
                                       3,1,7,3,['AccX','AccY','AccZ'])

        # TCP端口打开
        self.btn_tcp_open = ttk.Button(self.leftFrame, text='打开TCP', command=self.ev_btn_tcp_open)
        self.btn_tcp_open.pack(fill=tk.X)

        # 加速度计校准
        self.btn_acc_calibration = ttk.Button(self.leftFrame, text='加速度计校准', command=self.ev_btn_acc_calibration)
        self.btn_acc_calibration.pack(fill=tk.X)

        # 磁力计校准
        self.btn_compass_calibration = ttk.Button(self.leftFrame, text='磁力计校准', command=self.ev_btn_compass_calibration)
        self.btn_compass_calibration.pack(fill=tk.X)

        self.btn_test = ttk.Button(self.leftFrame, text='测试MSP',
                                                  command=self.ev_btn_test)
        self.btn_test.pack(fill=tk.X)



    def opencvThread(self):
        # 打开摄像头

        cap = cv2.VideoCapture(1)

        model = YOLO('weights/best.pt')

        # 开始采集和推流
        while True:
            # 采集一帧图像
            ret, frame = cap.read()

            results = model(frame)

            if len(results)>0:
                xyxy = results[0].boxes.xyxy.cpu().numpy().astype('uint32')
                print(xyxy)

            #annotated_image = results[0].plot()
            #resize_img = cv2.resize(annotated_image,dsize=(960,540))

            if ret:
                # 通过FFmpeg编码和推流
                cv2.imshow('myopencv', frame)

            key = cv2.waitKey(1)

            if key == ord('q') or key == 27:  # 'q'或者ESC都会触发退出条件
                break

        cap.release()
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    def ev_com_sel(self, e):
        sel = self.cbox.get()
        print(e, sel)

    def ev_btn_com_refresh(self):
        ports = myserial.list_ports()
        print(ports)
        self.cbox['value'] = ports

    def ev_btn_tcp_open(self):
        # 连接到服务器
        if self.tcpRunning:
            self.btn_tcp_open.config(text='打开')
        else:
            self.socket_client = socket.socket()
            self.socket_client.connect(("192.168.3.131", 8899))
            th = threading.Thread(target=self.thread_tcp_reading, args=())
            th.start()
            self.btn_tcp_open.config(text='关闭')
            
        self.tcpRunning = not self.tcpRunning

    def ev_btn_acc_calibration(self):
        #socket client...
        cmddata = [36,1,0,10]
        self.socket_client.sendall(bytes(cmddata))

    def ev_btn_compass_calibration(self):
        #socket client...
        cmddata = [36,2,0,10]
        self.socket_client.sendall(bytes(cmddata))

    def oneFrameEvent(self,oneframe):
        if oneframe[1]==1:
            #FlyState信息
            pitch = int.from_bytes(oneframe[3:5], 'little', signed=True) / 10.0
            roll = int.from_bytes(oneframe[5:7], 'little', signed=True) / 10.0
            yaw = int.from_bytes(oneframe[7:9], 'little', signed=True) / 10.0
            throttle = int.from_bytes(oneframe[9:11], 'little', signed=False)
            altitude = int.from_bytes(oneframe[11:13], 'little', signed=True)
            altitudeTarget = int.from_bytes(oneframe[13:15], 'little', signed=True)
            numSat = int.from_bytes(oneframe[15:17],'little',signed=False)
            altSonar = int.from_bytes(oneframe[17:19], 'little', signed=True)
            batteryVoltage = int.from_bytes(oneframe[19:21], 'little', signed=False)
            self.lbl_pitch.config(text=pitch)
            self.lbl_roll.config(text=roll)
            self.lbl_yaw.config(text=yaw)
            self.lbl_throttle.config(text=throttle)
            self.lbl_altitude.config(text=altitude)
            self.lbl_altitude_target.config(text=altitudeTarget)
            self.lbl_gpsNumSat.config(text=numSat)
            self.dcAltitude.put(altitude, altitudeTarget)
            self.lbl_altSonar.config(text=altSonar)
            self.lbl_batteryVoltage.config(text=batteryVoltage)
            self.dcAcc.put(pitch,roll,yaw)

    def thread_tcp_reading(self):
        time.sleep(1)
        while self.tcpRunning:
            try:
                recv_data = self.socket_client.recv(25)
                print(f"服务端回复的消息是：{recv_data}")
                self.dataParser.DecodeByBytes(recv_data)
            except Exception as e:
                print(e)

        self.socket_client.close()


    def thread_com_reading(self):
        while self.comm_service.is_opened():
            try:
                rdata = self.comm_service.read_comm()
                if rdata is not None:
                    self.dataParser.DecodeByOneByte(rdata)
            except Exception as e:
                print('err', e)

    def ev_btn_com_open(self):
        self.comm_service.set_comm_port(self.cbox.get())
        # btn_open.config(state=tk.DISABLED)
        if self.comm_service.is_opened():
            self.comm_service.close_comm()
            self.btn_open.config(text='打开')
        else:
            obj = self.comm_service.open_comm()
            if obj is None:
                print('打开串口失败!')
                return
            self.btn_open.config(text='关闭')

            th = threading.Thread(target=self.thread_com_reading, args=())
            th.start()

            robustOpticalFlow = RobustOpticalFlow(self.comm_service, 0)
            th = threading.Thread(target=robustOpticalFlow.run)
            th.start()

    def run(self):
        self.win.mainloop()

    def ev_btn_test(self):
        data = struct.pack('<BII',43,18942,986543)
        frame = self.mspHelper.buildOneFrame(0x1F02,data)
        self.comm_service.sendData(frame)